iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
自我挑戰組

Go in 3o系列 第 22

[Day22] - Go in 30 - 泛型(generic)

  • 分享至 

  • xImage
  •  

一、本篇提要

承接前篇泛型

  • 基本泛型概念
  • 泛型應用示範

二、基本泛型

2.1 基本泛型概念:

  • Type Parameter:作為佔位符存在的一個型別,用於表示在定義時尚未確定的具體型別。
  • Type Argument:實際傳遞給泛型型別的型別值,用於實例化泛型型別。
  • Type Constraint:限制型別形參可以接受的型別範圍。
  • Generic Type:定義中包含型別形參的型別,不能直接使用,需要實例化。

2.2 泛型應用:

你可以用泛型來定義可以接受不同型別的數據結構,如:Slice[T]。
透過傳入型別 Type Argument,泛型型別可以被實例化成具體的型別,如:Slice[int]。
透過型別約束 Type Constraint,可以限制型別形參的選擇,確保型別安全。

泛型示例:

假設我們目前有:

type stringSlice []string
var a stringSlice = []string{"A", "B", "C"} //沒錯
var b stringSlice = []int{1, 2, 3} //報錯

這是因為 stringSlice 型別為 []string,但如果今天我們想要,[]float64、[]int 呢 ?
也算簡單:

type stringSlice []string
type float64Slice []float64
type intSlice []int

但有更簡潔寫法 :

type Slice[T string|float64|int ] []T
  1. T : Type parameter,代表具體類型並不確定。

  2. string|float64|int 這部分被稱為 Type constraint,中間的 | 的意思是告訴編譯器,T 只可以接收 int 或 string 或 float64 這三種類型。

  3. []中括號裡的 T string|float64|int 這一整串因為定義了所有的 type parameters (在這個例子裡只有一個Type Parameter),所以我們稱其為 type parameter list 。

  4. 而這裡新定義的型別名稱叫 Slice[T]。

示範如果有多個 type parameter

假設我們需要定義一個Map函數,則該函數需要兩種類型:Key 和 Value。

type Map[K T, V T] struct {
    keys   []K
    values []V
}

在這個例子中,[K T, V T]是一個type parameter list。它包含了兩個type parameters:K 和 V。每個type parameter 都有自己的約束(在此例中,使用了 T 作為通用約束)。

如果想給每個type parameter加上特定的約束:

type Map[K string|int, V float64|bool] struct {
    keys   []K
    values []V
}

在這裡,K 可以是 string 或 int,而 V 可以是 float64 或 bool。

而這種帶有 type parameter 的型別,就被稱之為泛型(generic),且不能直接拿来使用,
需要傳入 Type argument 確定具體型別後才可用,而傳入具體型別的這個行為,稱做"實例化(Instantiations)"


var a Slice[T int|float32|float64] []T

// 這裡傳入了type parameter : int,泛型型別Slice[T]被實例化為 Slice[int]
var a Slice[int] = []int{1, 2, 3}  
fmt.Printf("Type Name: %T",a)  //輸出:Type Name: Slice[int]


// 傳入type parameter : float32, 將泛型型別Slice[T]實例化為 Slice[float32]
var b Slice[float32] = []float32{1.0, 2.0, 3.0} 
fmt.Printf("Type Name: %T",b)  //輸出:Type Name: Slice[float32]

// 錯誤。因為變數a的型別為Slice[int],b的型別為Slice[float32],兩者型別不同
a = b  

// ✗ 錯誤。string不在型別約束 int|float32|float64 中,不能用來實例化泛型型別
var c Slice[string] = []string{"Hello", "World"} 

// ✗ 錯誤。Slice[T]是泛型型別,不可直接使用必須實例化為具體的型別
var x Slice[T] = []int{1, 2, 3} 

範例 :

package main

import (
	"fmt"
)

type testMap[KEY string | int, VALUE float32 | float64 ] map[KEY]VALUE

func main() {
   
    var a testMap[string, float64] = map[string]float64{
        "jack": 75.3,
        "Ricky": 72.5,
    }
	// 取得 "jack" 的分數
	jackScore := a["jack"]
	fmt.Println("Jack's score:", jackScore)

	// 取得 "Ricky" 的分數
	rickyScore := a["Ricky"]
	fmt.Println("Ricky's score:", rickyScore)
}

2.4 基本泛型整理

https://ithelp.ithome.com.tw/upload/images/20231004/20162693vmKSqQJ1kV.jpg

參考 :
泛型
go doc


上一篇
[Day21] - Go in 30 - 介面 - 型別斷言(type assertion)
下一篇
[Day23] Go in 30 - 套件(package) - 簡介
系列文
Go in 3o30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言